#pragma once
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpClient.h>
#include <mutex>
#include "detail.hpp"
#include "fields.hpp"
#include "abstract.hpp"
#include "message.hpp"

namespace BRpc
{
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = std::shared_ptr<MuduoBuffer>;

    public:
        MuduoBuffer(muduo::net::Buffer *buf) : _buff(buf) {}
        virtual size_t readableBytes()
        {
            return _buff->readableBytes();
        }
        virtual int32_t peekInt32()
        {
            return _buff->peekInt32();
        }
        virtual int32_t readInt32()
        {
            return _buff->readInt32();
        }
        virtual void retrieveInt32()
        {
            return _buff->retrieveInt32();
        }
        virtual std::string retrieveAsString(size_t len)
        {
            return _buff->retrieveAsString(len);
        }

    private:
        // 不用智能指针的原因是 muduo库里面自己对缓冲区进行了安全的管理不用额外操作
        muduo::net::Buffer *_buff;
    };

    class BufferFactory
    {
    public:
        template <typename... Args>
        static BaseBuffer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...);
        }
    };

    class LVProtocol : public BaseProtocol
    {
        //|--len--|--Mtype--|--idlen--|--id--|--body--|
    public:
        using ptr = std::shared_ptr<LVProtocol>;

    public:
        virtual bool canProcessed(const BaseBuffer::ptr &buf) override
        {
            // 没有可读区域 直接返回
            ELOG("可读区域大小%ld",buf->readableBytes());
            if (buf->readableBytes() < len_value_field)
            {
                return false;
            }
            int32_t total_len = buf->peekInt32();
            // DLOG("total_len:%d", total_len);
            if (buf->readableBytes() < (total_len + len_value_field))
            {
                return false;
            }
            return true;
        }
        virtual bool onMessage(const BaseBuffer::ptr &buff, BaseMessage::ptr &msg) override
        {
            //|--len--|--Mtype--|--idlen--|--id--|--body--|
            // 调用onMessage的时候默认至少有一条信息可处理
            //ELOG("读取信息中");
            size_t value_len = buff->readInt32();
            MType mt = (MType)buff->readInt32();
            size_t id_len = buff->readInt32();
            size_t body_len = value_len - len_mtype_field - len_id_field - id_len;
            std::string id = buff->retrieveAsString(id_len);
            std::string body = buff->retrieveAsString(body_len);
            msg = MessageFactory::create(mt);
            if (msg.get() == nullptr)
            {
                ELOG("消息类型字段错误");
                return false;
            }
            bool ret = msg->unserialize(body);
            if (ret == false)
            {
                ELOG("消息正文序列化失败");
                return false;
            }
            msg->setId(id);
            msg->setMType(mt);
            return true;
        }
        virtual std::string serialize(const BaseMessage::ptr &msg) override
        {
            std::string body = msg->serialize();
            std::string id = msg->id();
            auto h_mt = (int32_t)msg->mtype();
            auto n_mt = htonl(h_mt);
            int32_t h_value_len = len_mtype_field + len_id_field + body.size() + id.size();
            DLOG("数据大小是%d",h_value_len);
            DLOG("信息大小是%d",body.size());
            DLOG("idd大小是%d",id.size());
            int32_t n_value_len = htonl(h_value_len);
            int32_t h_id_len=id.size();
            int32_t n_id_len = htonl(h_id_len); // htonl将主机字节序转换为网络字节序 避免因主机字节序不同而出现问题
            // 要传入网络字节序 不然会发生小变大的情况
            std::string result;
            result.reserve(len_value_field + h_value_len); // 提前预分配内存

            result.append(reinterpret_cast<const char *>(&n_value_len), len_value_field);
            result.append(reinterpret_cast<const char *>(&n_mt), len_mtype_field);
            result.append(reinterpret_cast<const char *>(&n_id_len), len_id_field);
            result.append(id);
            result.append(body);
            return result;
        }

    private:
        const size_t len_value_field = 4;
        const size_t len_mtype_field = 4;
        const size_t len_id_field = 4;
    };

    class ProtocolFactory
    {
    public:
        template <typename... Args>
        static BaseProtocol::ptr create(Args &&...args)
        {
            return std::make_shared<LVProtocol>(std::forward<Args>(args)...);
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = std::shared_ptr<MuduoConnection>;

    public:
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn, const BaseProtocol::ptr &protocol)
            : _conn(conn),
              _pro(protocol)
        {
        }
        virtual void send(const BaseMessage::ptr &msg) override
        {
            //一定要用协议对象去序列化信息 踩坑了！！！！！不要用消息本身的序列化
            std::string body = _pro->serialize(msg);
            DLOG("发送的数据的大小%d",body.size());
            _conn->send(body);
        }
        virtual void shutdown() override
        {
            _conn->shutdown();
        }
        virtual bool connected() override
        {
            return _conn->connected();
        }

    private:
        muduo::net::TcpConnectionPtr _conn;
        BaseProtocol::ptr _pro;
    };

    class ConnectionFactory
    {
    public:
        template <typename... Args>
        static BaseConnection::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };

    class MuduoServer : public BaseServer
    {
    public:
        using ptr = std::shared_ptr<MuduoServer>;

    public:
        MuduoServer(int port) : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "MuduoServer", muduo::net::TcpServer::kReusePort),//kResuePort这个参数是什么还不知道
                                _protocol(ProtocolFactory::create()) {}
        virtual void start() override
        {
            _server.setConnectionCallback(std::bind(&MuduoServer::onConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _server.start();
            _baseloop.loop();
        }
        // 连接建立时和连接断开时都会自动调用这个函数
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                std::cout << "connected success \n";
                BaseConnection::ptr muduo_conn = ConnectionFactory::create(conn, _protocol);
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _conns.insert(make_pair(conn, muduo_conn));
                }
                // 如果设置了连接回调函数调用
                if (_cb_connection)
                    _cb_connection(muduo_conn);
            }
            else
            {
                std::cout << "connection close \n";
                BaseConnection::ptr muduo_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it != _conns.end())
                    {
                        _conns.erase(conn);
                        muduo_conn = it->second;
                        if (_cb_close)
                            _cb_close(muduo_conn); // 这些回调函数还不知道 设置这个参数有什么用 看看后面视频会不会说
                    }
                }
            }
        }
        // 有信息到来时
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp t)
        {
            //DLOG("有信息来了")
            BaseBuffer::ptr base_buf = BufferFactory::create(buf);
            BaseProtocol::ptr protocol = ProtocolFactory::create();
            while (1)
            {
                if (protocol->canProcessed(base_buf) == false)
                {
                    // 缓冲区中一条完整信息都没有 但这条不完整的信息还超过了可读的最大大小时
                    if (buf->readableBytes() > max_read_size)
                    {
                        // 直接关闭连接 为激进做法
                        conn->shutdown();
                        ELOG("缓冲区中数据过大！");
                        return;
                    }
                    // 没有完整信息 直接等待下一次调用
                    break;
                }
                // 缓冲区中有一条完整信息
                //ELOG("有完整信息");
                BaseMessage::ptr msg;
                bool ret = protocol->onMessage(base_buf, msg);
                if (ret == false)
                {
                    // 直接关闭连接 为激进做法
                    conn->shutdown();
                    ELOG("缓冲区中数据错误！");
                    return;
                }
                // 消息反序列化成功
                BaseConnection::ptr base_conn;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        conn->shutdown();
                        return;
                    }
                    base_conn = it->second;
                }
                if (_cb_message)
                    _cb_message(base_conn, msg);
            }
        }

    private:
        const size_t max_read_size = 1024 * 64;
        muduo::net::EventLoop _baseloop;
        muduo::net::TcpServer _server;
        BaseProtocol::ptr _protocol;
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns; // 因为回调函数用的是我们自定义类型 所以为了找到所需对象而创建
        std::mutex _mutex;
    };

    class ServerFactory
    {
    public:
        template <typename... Args>
        static BaseServer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };

    class MuduoClient : public BaseClient
    {
    public:
        using ptr = std::shared_ptr<MuduoClient>;
        MuduoClient(const std::string &sip, int sport) : _protocol(ProtocolFactory::create()),
                                                         _baseloop(_loopthread.startLoop()),
                                                         _downlatch(1),
                                                         _client(_baseloop, muduo::net::InetAddress(sip, sport), "MuduoClient") {}
        virtual void connect() override
        {
            DLOG("设置回调函数，连接服务器");
            _client.setConnectionCallback(std::bind(&MuduoClient::onConnection, this, std::placeholders::_1));
            // 设置连接消息的回调
            _client.setMessageCallback(std::bind(&MuduoClient::onMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            // 连接服务器
            _client.connect();
            _downlatch.wait();
            DLOG("连接服务器成功！");
        }
        virtual void shutdown() override
        {
            return _client.disconnect();
        }
        virtual bool send(const BaseMessage::ptr &msg) override
        {
            if (connected() == false)
            {
                ELOG("连接已断开！");
                return false;
            }
            _conn->send(msg);
            return true;
        }
        virtual BaseConnection::ptr connection() override
        {
            return _conn;
        }
        virtual bool connected()
        {
            return (_conn && _conn->connected());
        }

    private:
        void onConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                std::cout << "连接建立！\n";
                _downlatch.countDown(); // 计数--，为0时唤醒阻塞
                _conn = ConnectionFactory::create(conn, _protocol);
            }
            else
            {
                std::cout << "连接断开！\n";
                _conn.reset();
            }
        }
        void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            DLOG("连接有数据到来，开始处理！");
            auto base_buf = BufferFactory::create(buf);
            while (1)
            {
                if (_protocol->canProcessed(base_buf) == false)
                {
                    // 数据不足
                    if (base_buf->readableBytes() > maxDataSize)
                    {
                        conn->shutdown();
                        ELOG("缓冲区中数据过大！");
                        return;
                    }
                    // DLOG("数据量不足！");
                    break;
                }
                // DLOG("缓冲区中数据可处理！");
                BaseMessage::ptr msg;
                bool ret = _protocol->onMessage(base_buf, msg);
                if (ret == false)
                {
                    conn->shutdown();
                    ELOG("缓冲区中数据错误！");
                    return;
                }
                // DLOG("缓冲区中数据解析完毕，调用回调函数进行处理！");
                if (_cb_message)
                    _cb_message(_conn, msg);
            }
        }

    private:
        const size_t maxDataSize = 1024 * 64;
        BaseProtocol::ptr _protocol;
        BaseConnection::ptr _conn;
        muduo::CountDownLatch _downlatch;
        muduo::net::EventLoopThread _loopthread;
        muduo::net::EventLoop *_baseloop;
        muduo::net::TcpClient _client;
    };

    class ClientFactory
    {
    public:
        template <typename... Args>
        static BaseClient::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };
}
/*
    服务器通信的流程
    moduoclient->send() send里面封装了BaseConnection里面的send
    BaseConnection里面的send使用muduo封装的TcpConnectionPtr里面的send
    这个send使用的字符串,我们要使用自定义协议的序列化将信息装变成我们的自定义协议
    这样我们使用自定义协议处理接收到的信息才能正确处理 不会发生粘包现象
    踩了一次坑在 自定义协议的序列化上 我一开始用了信息自身的序列化 也就是将json转换成字符串
*/